class HZRecorder {
    constructor(stream, config) {
        this.stream = stream
        this.config = config || {}
        this.config.sampleBits = this.config.sampleBits || 16
        this.config.sampleRate = this.config.sampleRate || 16000

        this.context = new (window.AudioContext || window.AudioContext)()
        this.audioInput = this.context.createMediaStreamSource(stream)
        const createScript = this.context.createScriptProcessor || this.context.createJavaScriptNode
        this.recorder = createScript.call(this.context, 4096, 1, 1)

        this.audioData = {
            size: 0,
            buffer: [],
            inputSampleRate: this.context.sampleRate,
            inputSampleBits: 16,
            outputSampleRate: this.config.sampleRate,
            oututSampleBits: this.config.sampleBits,
            input: function (data) {
                this.buffer.push(new Float32Array(data))
                this.size += data.length
            },
            compress: function () {
                const data = new Float32Array(this.size)
                let offset = 0
                for (let i = 0; i < this.buffer.length; i++) {
                    data.set(this.buffer[i], offset)
                    offset += this.buffer[i].length
                }
                const compression = parseInt(this.inputSampleRate / this.outputSampleRate)
                const length = data.length / compression
                const result = new Float32Array(length)
                let index = 0, j = 0
                while (index < length) {
                    result[index] = data[j]
                    j += compression
                    index++
                }
                return result
            },
            encodeWAV: function () {
                const sampleRate = Math.min(this.inputSampleRate, this.outputSampleRate)
                const sampleBits = Math.min(this.inputSampleBits, this.oututSampleBits)
                const bytes = this.compress()
                const dataLength = bytes.length * (sampleBits / 8)
                const buffer = new ArrayBuffer(44 + dataLength)
                const data = new DataView(buffer)
                let offset = 0

                const writeString = (str) => {
                    for (let i = 0; i < str.length; i++) {
                        data.setUint8(offset + i, str.charCodeAt(i))
                    }
                }

                writeString('RIFF'); offset += 4
                data.setUint32(offset, 36 + dataLength, true); offset += 4
                writeString('WAVE'); offset += 4
                writeString('fmt '); offset += 4
                data.setUint32(offset, 16, true); offset += 4
                data.setUint16(offset, 1, true); offset += 2
                data.setUint16(offset, 1, true); offset += 2
                data.setUint32(offset, sampleRate, true); offset += 4
                data.setUint32(offset, sampleRate * (sampleBits / 8), true); offset += 4
                data.setUint16(offset, sampleBits / 8, true); offset += 2
                data.setUint16(offset, sampleBits, true); offset += 2
                writeString('data'); offset += 4
                data.setUint32(offset, dataLength, true); offset += 4

                if (sampleBits === 8) {
                    for (let i = 0; i < bytes.length; i++, offset++) {
                        const s = Math.max(-1, Math.min(1, bytes[i]))
                        let val = s < 0 ? s * 0x8000 : s * 0x7FFF
                        val = parseInt(255 / (65535 / (val + 32768)))
                        data.setInt8(offset, val, true)
                    }
                } else {
                    for (let i = 0; i < bytes.length; i++, offset += 2) {
                        const s = Math.max(-1, Math.min(1, bytes[i]))
                        data.setInt16(offset, s < 0 ? s * 0x8000 : s * 0x7FFF, true)
                    }
                }

                return new Blob([data], { type: 'audio/wav' })
            }
        };

        this.recorder.onaudioprocess = (e) => {
            this.audioData.input(e.inputBuffer.getChannelData(0))
        };
    }

    start() {
        this.audioInput.connect(this.recorder)
        this.recorder.connect(this.context.destination)
    }

    stop() {
        this.recorder.disconnect()
    }

    inItsegment() {
        this.audioData.size = 0
        this.audioData.buffer = []
    }

    getBlob() {
        this.stop()
        this.closeMike()
        return this.audioData.encodeWAV()
    }

    play(audio) {
        const blob = this.getBlob()
        audio.src = URL.createObjectURL(blob)
    }

    upload() {
        return this.getBlob()
    }

    closeMike() {
        this.stream.getTracks().forEach(track => track.stop())
    }
}

export default HZRecorder